-
Notifications
You must be signed in to change notification settings - Fork 403
feat(backend, clerk-js): Update the supported API version to 2025-11-10
#7095
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(backend, clerk-js): Update the supported API version to 2025-11-10
#7095
Conversation
🦋 Changeset detectedLatest commit: 1d1b602 The changes in this PR will be included in the next version bump. This PR includes changesets to release 14 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis PR updates API version constants to '2025-11-10', broadens many billing-related types and JSON shapes to be optional/null, adjusts backend resource mappers and frontend resource classes to handle nullable fields, switches UI checks from numeric amount tests to presence checks, renames a checkout param (paymentSourceId → paymentMethodId), and updates tests and a changeset. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as Billing UI
participant Checkout as Checkout Flow
participant Mapper as JSON → Resource Mapper
participant API as Billing Service
Note over UI,Checkout: User views plans / opens checkout
UI->>Checkout: request checkout/plan data
Checkout->>API: fetch JSON payload
API-->>Mapper: returns JSON (nullable/optional fields)
Mapper->>Mapper: normalize nullable fields, convert epochs → Date
Mapper-->>Checkout: BillingCheckoutResource / BillingPlanResource (nullable-aware)
Checkout-->>UI: render line items and toggles (use presence checks like Boolean(annualMonthlyFee))
alt payment required
UI->>API: confirmCheckout (payload uses paymentMethodId)
API-->>UI: success / failure
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Areas to focus on:
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Disabled knowledge base sources:
📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (9)**/*.{js,jsx,ts,tsx}📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Files:
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Files:
packages/**/*.{ts,tsx}📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Files:
packages/**/*.{ts,tsx,d.ts}📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Files:
**/*.{ts,tsx}📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Files:
**/*.{jsx,tsx}📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Files:
**/*.tsx📄 CodeRabbit inference engine (.cursor/rules/react.mdc)
Files:
**/*.test.{jsx,tsx}📄 CodeRabbit inference engine (.cursor/rules/react.mdc)
Files:
**/__tests__/**/*.{ts,tsx}📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)
Files:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
🔇 Additional comments (2)
Comment |
2025-10-012025-10-01
2025-10-012025-10-01
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
packages/backend/src/constants.ts(1 hunks)packages/clerk-js/src/core/constants.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/clerk-js/src/core/constants.tspackages/backend/src/constants.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/clerk-js/src/core/constants.tspackages/backend/src/constants.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/clerk-js/src/core/constants.tspackages/backend/src/constants.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/clerk-js/src/core/constants.tspackages/backend/src/constants.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertionsfor literal types:as const
Usesatisfiesoperator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/clerk-js/src/core/constants.tspackages/backend/src/constants.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/clerk-js/src/core/constants.tspackages/backend/src/constants.ts
🔇 Additional comments (1)
packages/clerk-js/src/core/constants.ts (1)
57-57: Version update is properly integrated and tested.The verification confirms that
SUPPORTED_FAPI_VERSION = '2025-10-01'is correctly implemented:
- The version is consistently passed as
__clerk_api_versionquery parameter in all API requests viafapiClient.buildUrl()- Comprehensive test coverage (15+ test cases in
fapiClient.test.ts) confirms the version is correctly appended to request URLs- Type definitions in
@clerk/typesare generically designed to handle versioned API responses without requiring version-specific schema switchingThe frontend does not handle version-dependent response schema changes—this responsibility belongs to the backend, which validates and returns appropriate responses based on the
__clerk_api_versionparameter. The generic type system ensures compatibility across API versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
.changeset/hot-jars-smell.md(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/**
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Automated releases must use Changesets.
Files:
.changeset/hot-jars-smell.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep-cloud-platform/scan
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/elements
@clerk/clerk-expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/clerk-react
@clerk/react-router
@clerk/remix
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/themes
@clerk/types
@clerk/upgrade
@clerk/vue
commit: |
2025-10-012025-10-01
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
🧹 Nitpick comments (1)
packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx (1)
159-163: Consider adding non-null assertion for type safety.The
planFeecalculation at line 162 may cause a TypeScript error becauseplan.annualMonthlyFeeis inferred asBillingMoneyAmount | null, even though the logic ensures it's non-null when reached.Consider updating the code to match the pattern in PricingTableDefault.tsx:
- const planFee = !plan.annualMonthlyFee - ? plan.fee - : planPeriod === 'annual' - ? plan.annualMonthlyFee - : plan.fee; + const planFee = !plan.annualMonthlyFee + ? plan.fee + : planPeriod === 'annual' + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + plan.annualMonthlyFee! + : plan.fee;
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (13)
packages/backend/src/tokens/__tests__/handshake.test.ts(1 hunks)packages/clerk-js/src/core/resources/BillingPlan.ts(2 hunks)packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx(1 hunks)packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx(1 hunks)packages/clerk-js/src/ui/components/Plans/PlanDetails.tsx(2 hunks)packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx(1 hunks)packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx(3 hunks)packages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.ts(1 hunks)packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx(3 hunks)packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx(1 hunks)packages/clerk-js/src/ui/contexts/components/Plans.tsx(2 hunks)packages/types/src/billing.ts(1 hunks)packages/types/src/json.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/backend/src/tokens/tests/handshake.test.ts
🧰 Additional context used
📓 Path-based instructions (9)
packages/clerk-js/src/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/clerk-js-ui.mdc)
packages/clerk-js/src/ui/**/*.{ts,tsx}: Element descriptors should always be camelCase
Use element descriptors in UI components to enable consistent theming and styling via appearance.elements
Element descriptors should generate unique, stable CSS classes for theming
Element descriptors should handle state classes (e.g., cl-loading, cl-active, cl-error, cl-open) automatically based on component state
Do not render hard-coded values; all user-facing strings must be localized using provided localization methods
Use the useLocalizations hook and localizationKeys utility for all text and error messages
Use the styled system (sx prop, theme tokens, responsive values) for custom component styling
Use useCardState for card-level state, useFormState for form-level state, and useLoadingStatus for loading states
Always use handleError utility for API errors and use translateError for localized error messages
Use useFormControl for form field state, implement proper validation, and handle loading and error states in forms
Use localization keys for all form labels and placeholders
Use element descriptors for consistent styling and follow the theme token system
Use the Card and FormContainer patterns for consistent UI structure
Files:
packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/types/src/json.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/types/src/json.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/types/src/json.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/types/src/json.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertionsfor literal types:as const
Usesatisfiesoperator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/types/src/json.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{jsx,tsx}: Use error boundaries in React components
Minimize re-renders in React components
**/*.{jsx,tsx}: Always use functional components with hooks instead of class components
Follow PascalCase naming for components:UserProfile,NavigationMenu
Keep components focused on a single responsibility - split large components
Limit component size to 150-200 lines; extract logic into custom hooks
Use composition over inheritance - prefer smaller, composable components
Export components as named exports for better tree-shaking
One component per file with matching filename and component name
Use useState for simple state management
Use useReducer for complex state logic
Implement proper state initialization
Use proper state updates with callbacks
Implement proper state cleanup
Use Context API for theme/authentication
Implement proper state selectors
Use proper state normalization
Implement proper state persistence
Use React.memo for expensive components
Implement proper useCallback for handlers
Use proper useMemo for expensive computations
Implement proper virtualization for lists
Use proper code splitting with React.lazy
Implement proper cleanup in useEffect
Use proper refs for DOM access
Implement proper event listener cleanup
Use proper abort controllers for fetch
Implement proper subscription cleanup
Use proper HTML elements
Implement proper ARIA attributes
Use proper heading hierarchy
Implement proper form labels
Use proper button types
Implement proper focus management
Use proper keyboard shortcuts
Implement proper tab order
Use proper skip links
Implement proper focus traps
Implement proper error boundaries
Use proper error logging
Implement proper error recovery
Use proper error messages
Implement proper error fallbacks
Use proper form validation
Implement proper error states
Use proper error messages
Implement proper form submission
Use proper form reset
Use proper component naming
Implement proper file naming
Use proper prop naming
Implement proper...
Files:
packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/types/src/json.tspackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/react.mdc)
**/*.tsx: Use proper type definitions for props and state
Leverage TypeScript's type inference where possible
Use proper event types for handlers
Implement proper generic types for reusable components
Use proper type guards for conditional rendering
Files:
packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/contexts/components/Plans.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
🧬 Code graph analysis (2)
packages/clerk-js/src/core/resources/BillingPlan.ts (2)
packages/types/src/billing.ts (1)
BillingMoneyAmount(615-632)packages/clerk-js/src/utils/billing.ts (1)
billingMoneyAmountFromJSON(10-17)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (1)
packages/clerk-js/src/ui/contexts/components/Plans.tsx (1)
normalizeFormatted(27-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: Build Packages
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (12)
packages/clerk-js/src/core/resources/BillingPlan.ts (2)
11-12: LGTM: Nullable field declarations are correct.Making
annualFeeandannualMonthlyFeenullable with defaultnullproperly reflects the API changes and allows plans without annual pricing options.
44-45: LGTM: Proper null-safe JSON deserialization.The ternary operator correctly handles nullable annual pricing fields, setting them to
nullwhen absent from the API response.packages/types/src/json.ts (1)
637-638: LGTM: Type definitions correctly reflect nullable annual pricing.Making
annual_feeandannual_monthly_feenullable in the JSON type definitions properly aligns with the API version 2025-10-01 changes.packages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.ts (1)
37-37: LGTM: Presence-based check is safer.Switching from
plan.annualMonthlyFee.amount > 0toBoolean(plan.annualMonthlyFee)is the correct approach for nullable fields. This avoids potential null reference errors and properly handles the updated data model.packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (1)
377-378: LGTM: Safe presence check for switchability.Using
Boolean(subscription.plan.annualMonthlyFee)is the correct approach to check if annual pricing is available before allowing period switching.packages/clerk-js/src/ui/components/Plans/PlanDetails.tsx (2)
229-234: LGTM: Proper null handling with fallback.This code correctly checks for
annualMonthlyFeeexistence before using it, falling back toplan.feewhen it's null. This is the safe pattern that should be used throughout the codebase.
336-364: LGTM: Conditional rendering based on existence.Using
plan.annualMonthlyFeein a conditional expression properly handles nullable fields. The toggle is only shown when annual pricing is available, with an appropriate notice displayed otherwise.packages/types/src/billing.ts (1)
134-140: LGTM! Type definitions accurately reflect nullable annual pricing.The type changes for
annualFeeandannualMonthlyFeeto include| nullare well-documented and align with the broader API version update. Since the interface is marked as@experimental, this change is appropriate.packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx (1)
287-296: LGTM! Non-null assertion is safe in this context.The fee calculation correctly handles nullable
annualMonthlyFee. The non-null assertion on line 294 is safe becauseplanSupportsAnnualis onlytruewhenannualMonthlyFeeis truthy.packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx (2)
63-63: Consistent with presence-based logic across the PR.The change to
Boolean(plan.annualMonthlyFee)aligns with the nullable type updates. This enables billing cycle controls whenannualMonthlyFeeexists, regardless of amount.
255-297: LGTM! Conditional rendering correctly handles nullable field.The truthiness check
plan.annualMonthlyFee ? (...)appropriately renders the annual billing notice only when annual pricing exists.packages/clerk-js/src/ui/contexts/components/Plans.tsx (1)
216-216: LGTM! Correctly checks for annual pricing eligibility.The use of
Boolean(plan?.annualMonthlyFee)with optional chaining safely checks whether a plan supports annual billing.
| const fee = | ||
| planPeriod === 'month' | ||
| ? plan.fee | ||
| : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| plan.annualMonthlyFee!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsafe non-null assertion could cause runtime errors.
Same issue as in PaymentAttemptPage: using plan.annualMonthlyFee! without verification could throw if the field is null.
Apply this safer pattern:
- const fee =
- planPeriod === 'month'
- ? plan.fee
- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- plan.annualMonthlyFee!;
+ const fee =
+ planPeriod === 'month' || !plan.annualMonthlyFee
+ ? plan.fee
+ : plan.annualMonthlyFee;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const fee = | |
| planPeriod === 'month' | |
| ? plan.fee | |
| : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
| plan.annualMonthlyFee!; | |
| const fee = | |
| planPeriod === 'month' || !plan.annualMonthlyFee | |
| ? plan.fee | |
| : plan.annualMonthlyFee; |
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx around lines 42
to 46, the code uses an unsafe non-null assertion plan.annualMonthlyFee! which
can throw at runtime if that field is null; replace the assertion with a guarded
access and explicit handling: check whether plan.annualMonthlyFee is defined
before using it (e.g., use a conditional/fallback value or throw a descriptive
error), or compute fee via a nullish coalescing/fallback expression so fee is
never assigned an unchecked null value; ensure TypeScript types reflect the
runtime check so there are no non-null assertions left.
| const fee = | ||
| subscriptionItem.planPeriod === 'month' ? subscriptionItem.plan.fee : subscriptionItem.plan.annualMonthlyFee; | ||
| subscriptionItem.planPeriod === 'month' | ||
| ? subscriptionItem.plan.fee | ||
| : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| subscriptionItem.plan.annualMonthlyFee!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsafe non-null assertion could cause runtime errors.
The code uses a non-null assertion (plan.annualMonthlyFee!) without first verifying that annualMonthlyFee exists. Since this field is now nullable (as per the type changes), this could throw a runtime error if the API returns null.
Apply this defensive pattern instead:
- const fee =
- subscriptionItem.planPeriod === 'month'
- ? subscriptionItem.plan.fee
- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- subscriptionItem.plan.annualMonthlyFee!;
+ const fee =
+ subscriptionItem.planPeriod === 'month' || !subscriptionItem.plan.annualMonthlyFee
+ ? subscriptionItem.plan.fee
+ : subscriptionItem.plan.annualMonthlyFee;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const fee = | |
| subscriptionItem.planPeriod === 'month' ? subscriptionItem.plan.fee : subscriptionItem.plan.annualMonthlyFee; | |
| subscriptionItem.planPeriod === 'month' | |
| ? subscriptionItem.plan.fee | |
| : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
| subscriptionItem.plan.annualMonthlyFee!; | |
| const fee = | |
| subscriptionItem.planPeriod === 'month' || !subscriptionItem.plan.annualMonthlyFee | |
| ? subscriptionItem.plan.fee | |
| : subscriptionItem.plan.annualMonthlyFee; |
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx
around lines 217-221, the code uses a non-null assertion on
subscriptionItem.plan.annualMonthlyFee which is now nullable; replace the unsafe
assertion with a defensive check: if annualMonthlyFee is present use it,
otherwise fall back to a safe default (e.g., plan.fee, 0, or a user-friendly
placeholder) or throw/log a clear error before using the value so no runtime
null access occurs. Ensure the code handles both month and non-month periods
without the `!` operator and preserves type-safety.
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx
Show resolved
Hide resolved
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| price: normalizeFormatted(subscription.plan.annualFee!.amountFormatted), | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| currency: subscription.plan.annualFee!.currencySymbol, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsafe non-null assertion in localization keys.
The non-null assertions for subscription.plan.annualFee! are unsafe. If annualFee is null, this will throw at runtime when constructing the localization string.
Since this code path only executes when isSwitchable is true (which checks for annualMonthlyFee), you should also verify annualFee exists:
+ // Ensure annualFee exists before using it
+ if (!subscription.plan.annualFee) {
+ return null;
+ }
isSwitchable
? {
label:
subscription.planPeriod === 'month'
? localizationKeys('billing.switchToAnnualWithAnnualPrice', {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- price: normalizeFormatted(subscription.plan.annualFee!.amountFormatted),
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- currency: subscription.plan.annualFee!.currencySymbol,
+ price: normalizeFormatted(subscription.plan.annualFee.amountFormatted),
+ currency: subscription.plan.annualFee.currencySymbol,
})Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx around
lines 412 to 415, the code uses non-null assertions on
subscription.plan.annualFee which can throw if annualFee is null; add a guard
that verifies subscription.plan.annualFee exists before accessing
amountFormatted and currencySymbol (e.g., if (!subscription.plan.annualFee)
return/skip building the localized string or provide safe fallbacks), and
replace the non-null assertions with safe access or validated locals so the
localization string is only built when annualFee is present.
| const fee = | ||
| subscription.planPeriod === 'month' | ||
| ? subscription.plan.fee | ||
| : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| subscription.plan.annualFee!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsafe non-null assertion in fee calculation.
Using subscription.plan.annualFee! without verification could throw if the field is null.
Apply this safer pattern:
- const fee =
- subscription.planPeriod === 'month'
- ? subscription.plan.fee
- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- subscription.plan.annualFee!;
+ const fee =
+ subscription.planPeriod === 'month' || !subscription.plan.annualFee
+ ? subscription.plan.fee
+ : subscription.plan.annualFee;🤖 Prompt for AI Agents
In packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx around
lines 485 to 489, the code uses a non-null assertion
subscription.plan.annualFee! which can throw if annualFee is null/undefined;
replace the assertion with a safe check — use optional chaining and a fallback
(e.g., subscription.plan.annualFee ?? 0 or another sensible default), or
explicitly handle the missing value (throw a descriptive error or return early).
Ensure fee is computed like: if planPeriod === 'month' use
subscription.plan.fee, otherwise use subscription.plan.annualFee ?? <fallback>
so no non-null assertion is needed.
6ac7b4d to
52a2409
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
♻️ Duplicate comments (4)
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx (1)
42-46: Unsafe non-null assertion could cause runtime errors.The non-null assertion
plan.annualMonthlyFee!on line 46 is unsafe. SinceannualMonthlyFeeis now nullable (as seen in the type updates inpackages/types/src/billing.ts), this will throw a runtime error if the field isnull.Apply the safer pattern suggested in the previous review:
- const fee = - planPeriod === 'month' - ? plan.fee - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - plan.annualMonthlyFee!; + const fee = + planPeriod === 'month' || !plan.annualMonthlyFee + ? plan.fee + : plan.annualMonthlyFee;packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (2)
412-415: Unsafe non-null assertion in localization keys.The non-null assertions for
subscription.plan.annualFee!on lines 413 and 415 are unsafe. IfannualFeeisnull, this will throw at runtime.As suggested in the previous review, add a guard before this code path:
+ // Ensure annualFee exists before using it + if (subscription.planPeriod === 'month' && !subscription.plan.annualFee) { + return []; + } isSwitchable ? { label: subscription.planPeriod === 'month' ? localizationKeys('billing.switchToAnnualWithAnnualPrice', { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - price: normalizeFormatted(subscription.plan.annualFee!.amountFormatted), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - currency: subscription.plan.annualFee!.currencySymbol, + price: normalizeFormatted(subscription.plan.annualFee.amountFormatted), + currency: subscription.plan.annualFee.currencySymbol, })
485-489: Unsafe non-null assertion in fee calculation.Using
subscription.plan.annualFee!without verification on line 489 could throw if the field isnull.Apply this safer pattern:
- const fee = - subscription.planPeriod === 'month' - ? subscription.plan.fee - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - subscription.plan.annualFee!; + const fee = + subscription.planPeriod === 'month' || !subscription.plan.annualFee + ? subscription.plan.fee + : subscription.plan.annualFee;packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx (1)
285-285: Critical: Incorrect logic allows zero-cost annual plans to show annual toggle.Line 285 changed to check
Boolean(annualMonthlyFee)instead ofannualMonthlyFee.amount > 0. This breaks the intended behavior: plans with annual pricing set to 0 ({amount: 0, ...}) will now be treated as supporting annual periods when they shouldn't.The test in
pricing-footer-state.spec.tsline 108 explicitly expects: "does not show footer when switching period if annualMonthlyFee is 0".- const planSupportsAnnual = Boolean(annualMonthlyFee); + const planSupportsAnnual = Boolean(annualMonthlyFee && annualMonthlyFee.amount > 0);Note: This same pattern also appears in:
Plans.tsx(lines 216, 329)pricing-footer-state.ts(line 37)PricingTableMatrix.tsx(lines 63, 159)Verify and update those locations as well.
Based on learnings
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
packages/clerk-js/src/core/resources/BillingPlan.ts(2 hunks)packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx(1 hunks)packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx(1 hunks)packages/clerk-js/src/ui/components/Plans/PlanDetails.tsx(2 hunks)packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsx(2 hunks)packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx(1 hunks)packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx(3 hunks)packages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.ts(1 hunks)packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx(1 hunks)packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx(3 hunks)packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx(1 hunks)packages/clerk-js/src/ui/contexts/components/Plans.tsx(2 hunks)packages/clerk-js/src/utils/billing.ts(1 hunks)packages/types/src/billing.ts(1 hunks)packages/types/src/json.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- packages/clerk-js/src/ui/contexts/components/Plans.tsx
- packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx
- packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx
- packages/types/src/json.ts
- packages/clerk-js/src/ui/components/Plans/PlanDetails.tsx
- packages/clerk-js/src/core/resources/BillingPlan.ts
- packages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.ts
🧰 Additional context used
📓 Path-based instructions (13)
packages/clerk-js/src/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/clerk-js-ui.mdc)
packages/clerk-js/src/ui/**/*.{ts,tsx}: Element descriptors should always be camelCase
Use element descriptors in UI components to enable consistent theming and styling via appearance.elements
Element descriptors should generate unique, stable CSS classes for theming
Element descriptors should handle state classes (e.g., cl-loading, cl-active, cl-error, cl-open) automatically based on component state
Do not render hard-coded values; all user-facing strings must be localized using provided localization methods
Use the useLocalizations hook and localizationKeys utility for all text and error messages
Use the styled system (sx prop, theme tokens, responsive values) for custom component styling
Use useCardState for card-level state, useFormState for form-level state, and useLoadingStatus for loading states
Always use handleError utility for API errors and use translateError for localized error messages
Use useFormControl for form field state, implement proper validation, and handle loading and error states in forms
Use localization keys for all form labels and placeholders
Use element descriptors for consistent styling and follow the theme token system
Use the Card and FormContainer patterns for consistent UI structure
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/utils/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/utils/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/utils/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/utils/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertionsfor literal types:as const
Usesatisfiesoperator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/utils/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{jsx,tsx}: Use error boundaries in React components
Minimize re-renders in React components
**/*.{jsx,tsx}: Always use functional components with hooks instead of class components
Follow PascalCase naming for components:UserProfile,NavigationMenu
Keep components focused on a single responsibility - split large components
Limit component size to 150-200 lines; extract logic into custom hooks
Use composition over inheritance - prefer smaller, composable components
Export components as named exports for better tree-shaking
One component per file with matching filename and component name
Use useState for simple state management
Use useReducer for complex state logic
Implement proper state initialization
Use proper state updates with callbacks
Implement proper state cleanup
Use Context API for theme/authentication
Implement proper state selectors
Use proper state normalization
Implement proper state persistence
Use React.memo for expensive components
Implement proper useCallback for handlers
Use proper useMemo for expensive computations
Implement proper virtualization for lists
Use proper code splitting with React.lazy
Implement proper cleanup in useEffect
Use proper refs for DOM access
Implement proper event listener cleanup
Use proper abort controllers for fetch
Implement proper subscription cleanup
Use proper HTML elements
Implement proper ARIA attributes
Use proper heading hierarchy
Implement proper form labels
Use proper button types
Implement proper focus management
Use proper keyboard shortcuts
Implement proper tab order
Use proper skip links
Implement proper focus traps
Implement proper error boundaries
Use proper error logging
Implement proper error recovery
Use proper error messages
Implement proper error fallbacks
Use proper form validation
Implement proper error states
Use proper error messages
Implement proper form submission
Use proper form reset
Use proper component naming
Implement proper file naming
Use proper prop naming
Implement proper...
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
packages/**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Unit tests should use Jest or Vitest as the test runner.
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx
packages/{clerk-js,elements,themes}/**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Visual regression testing should be performed for UI components.
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/utils/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/types/src/billing.tspackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/react.mdc)
**/*.tsx: Use proper type definitions for props and state
Leverage TypeScript's type inference where possible
Use proper event types for handlers
Implement proper generic types for reusable components
Use proper type guards for conditional rendering
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx
**/*.test.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/react.mdc)
**/*.test.{jsx,tsx}: Use React Testing Library
Test component behavior, not implementation
Use proper test queries
Implement proper test isolation
Use proper test coverage
Test component interactions
Use proper test data
Implement proper test setup
Use proper test cleanup
Implement proper test assertions
Use proper test structure
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx
**/__tests__/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)
**/__tests__/**/*.{ts,tsx}: Create type-safe test builders/factories
Use branded types for test isolation
Implement proper mock types that match interfaces
Files:
packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx
🧬 Code graph analysis (1)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (1)
packages/clerk-js/src/ui/contexts/components/Plans.tsx (1)
normalizeFormatted(27-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (3)
packages/clerk-js/src/ui/components/SubscriptionDetails/__tests__/SubscriptionDetails.test.tsx (1)
265-266: LGTM! Test data correctly reflects nullable annual pricing.The change to null values for
annualFeeandannualMonthlyFeein the Free Plan scenario correctly aligns with the type updates that made these fields nullable. This ensures tests cover the case where annual pricing is not available.packages/clerk-js/src/ui/components/Plans/__tests__/PlanDetails.test.tsx (1)
224-225: LGTM! Test data correctly reflects nullable annual pricing.The changes to null values for
annualFeeandannualMonthlyFeeproperly test scenarios where annual pricing is unavailable. This aligns with the type updates inpackages/types/src/billing.tsthat made these fields nullable.Also applies to: 258-259
packages/clerk-js/src/utils/billing.ts (1)
28-36: The original review comment is based on incorrect reasoning about JavaScript truthiness.The truthy checks on lines 28, 31, and 34 will not exclude zero-amount values. Since
BillingMoneyAmountJSONis an object (e.g.,{amount: 0, amountFormatted: '0.00', ...}), and objects are truthy in JavaScript, these conditions will evaluate to true and the assignments will occur. The type definitions confirm these fields are required (not nullable) inBillingCheckoutTotalsJSONandBillingStatementTotalsJSON, and extensive test coverage demonstrates zero-amount objects are expected in the output.The distinction between the original
incheck and the new truthy check only matters if the value isnull,undefined, or a falsy scalar—not for objects with zero-amount properties inside them.Likely an incorrect or invalid review comment.
packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx
Show resolved
Hide resolved
| // WHY `total_due_after_free_trial` and why `proration` | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarify the purpose of this comment.
The comment suggests uncertainty about total_due_after_free_trial and proration fields. If these fields are part of the API response but not being handled, document why they're excluded or create a task to implement them.
🤖 Prompt for AI Agents
In packages/clerk-js/src/utils/billing.ts around lines 38 to 39, the inline
comment "// WHY `total_due_after_free_trial` and why `proration`" is vague;
replace it with a clear explanation stating whether those fields appear in the
API response and why they are intentionally ignored (e.g., not used by current
billing flow), or if they must be supported, add a TODO with a link/ID to a
tracker ticket to implement handling. Include what the fields represent, the
decision (exclude or implement), and next steps (either remove/comment as
explained or add task to implement parsing/logic).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/types/src/billing.ts (1)
691-703: Changeset must document the breaking change with explicit migration guidance.The changeset file (
./.changeset/hot-jars-smell.md) only states "Update the supported API version to2025-10-01" but fails to document the breaking change: renamingpaymentSourceIdtopaymentMethodIdinConfirmCheckoutParams.While the codebase has been correctly updated (no references to the old parameter remain), the changeset lacks the necessary migration guidance that users need to update their code. This differs from the pattern established in v4.93.0, which documented similar billing API renames with clear before/after examples.
Update the changeset to include:
- Explicit mention of the parameter rename:
paymentSourceId→paymentMethodId- Migration guidance: "Rename
paymentSourceIdtopaymentMethodIdin all checkoutconfirm()calls"
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx(2 hunks)packages/types/src/billing.ts(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/types/src/billing.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/types/src/billing.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/types/src/billing.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/types/src/billing.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertionsfor literal types:as const
Usesatisfiesoperator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/types/src/billing.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/types/src/billing.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep-cloud-platform/scan
| * The annual price of the plan or `null` if the plan is not annual. | ||
| */ | ||
| annualFee: BillingMoneyAmount; | ||
| annualFee: BillingMoneyAmount | null; | ||
| /** | ||
| * The effective monthly price when billed annually. | ||
| * The effective monthly price when billed annually or `null` if the plan is not annual. | ||
| */ | ||
| annualMonthlyFee: BillingMoneyAmount; | ||
| annualMonthlyFee: BillingMoneyAmount | null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify that consumer code was updated per previous review findings.
The nullable type change for annualFee and annualMonthlyFee is correct, but a previous review identified specific locations with non-null assertions and unsafe Boolean checks that will break with this change.
Ensure all flagged locations have been fixed before merging:
- 7 non-null assertion sites (e.g.,
SubscriptionsList.tsx:157,SubscriptionDetails/index.tsx:413,415,489) - 3 unsafe Boolean checks (e.g.,
PricingTableMatrix.tsx:63,pricing-footer-state.ts:37)
Based on learnings
🏁 Script executed:
#!/bin/bash
# Verify that non-null assertions and unsafe Boolean checks have been addressed
echo "=== Checking for remaining non-null assertions on annual fees ==="
rg -n 'annualFee!' --type=ts -C2
echo ""
echo "=== Checking for remaining non-null assertions on annualMonthlyFee ==="
rg -n 'annualMonthlyFee!' --type=ts -C2
echo ""
echo "=== Checking for unsafe Boolean checks without null guards ==="
rg -nP 'if\s*\([^)]*\.(annualFee|annualMonthlyFee)\)' --type=ts -C2
echo ""
echo "=== Checking for direct property access without null checks ==="
rg -n '\.(annualFee|annualMonthlyFee)\.(amount|amountFormatted|currency)' --type=ts -C3Length of output: 4581
Address unsafe non-null assertions in consumer code to properly handle nullable annual fees.
The nullable type changes for annualFee and annualMonthlyFee are correct, but consumer code still uses non-null assertions (!) that bypass TypeScript's type safety. These should be replaced with proper null checks or type guards:
SubscriptionsList.tsx:157:subscription.plan.annualFee!SubscriptionDetails/index.tsx:413, 415, 489:annualFee!and property accessPricingTableDefault.tsx:294:plan.annualMonthlyFee!PaymentAttemptPage.tsx:221:plan.annualMonthlyFee!CheckoutForm.tsx:46:plan.annualMonthlyFee!
While runtime logic (e.g., planPeriod === 'annual') ensures these values exist in practice, the type system doesn't capture these guarantees. Refactor to use discriminated unions, type predicates, or explicit null checks instead of non-null assertions.
🤖 Prompt for AI Agents
In packages/types/src/billing.ts around lines 134-140, avoid forcing non-null
with `!` in consumers by either making the Plan type a discriminated union
(e.g., separate MonthlyPlan and AnnualPlan where annualFee/annualMonthlyFee
exist only on AnnualPlan with a literal period discriminator) or keep the
nullable fields but update the listed consumer files to narrow the type before
access: replace `!` usages with explicit null checks or type guards (e.g., if
(plan.period === 'annual') { /* safe to access annualFee/annualMonthlyFee */ })
so TypeScript can verify safety and remove the non-null assertions in
SubscriptionsList.tsx:157, SubscriptionDetails/index.tsx:413,415,489,
PricingTableDefault.tsx:294, PaymentAttemptPage.tsx:221, and
CheckoutForm.tsx:46.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (7)
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx (1)
61-70: Drop the non-null assertion onplan.annualMonthlyFee.
This is the same crash we flagged in the earlier review: the annual-plan path still dereferencesplan.annualMonthlyFee!, so when the new API returnsnullthe checkout totals render blows up before we can show the form. Guard the value instead of asserting so we fail soft and keep the UI usable.Apply this diff:
- const fee = - planPeriod === 'month' - ? plan.fee - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - plan.annualMonthlyFee!; + const fee = + planPeriod === 'month' + ? plan.fee + : plan.annualMonthlyFee ?? plan.fee;packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx (1)
217-221: Critical: Unsafe non-null assertion can cause runtime errors.The non-null assertion
annualMonthlyFee!suppresses TypeScript warnings but does not prevent runtime errors when the field is null. SinceannualMonthlyFeeis now nullable (per the API version update), this code will throw ifplanPeriodis not'month'butannualMonthlyFeeis null.Apply this defensive pattern:
const fee = - subscriptionItem.planPeriod === 'month' + subscriptionItem.planPeriod === 'month' || !subscriptionItem.plan.annualMonthlyFee ? subscriptionItem.plan.fee - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - subscriptionItem.plan.annualMonthlyFee!; + : subscriptionItem.plan.annualMonthlyFee;packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx (2)
285-285: Critical: Boolean check breaks zero-cost annual pricing.The logic changed from checking
annualMonthlyFee.amount > 0toBoolean(annualMonthlyFee). This treats plans with{ amount: 0, ... }as supporting annual periods, but the test inpricing-footer-state.spec.tsline 108 ("does not show footer when switching period if annualMonthlyFee is 0") confirms zero-cost annual plans should not enable the toggle.Apply this fix:
-const planSupportsAnnual = Boolean(annualMonthlyFee); +const planSupportsAnnual = Boolean(annualMonthlyFee?.amount);
292-295: Critical: Unsafe non-null assertion when annual pricing enabled.If
planSupportsAnnualis true butannualMonthlyFeeis somehow null (data inconsistency), the non-null assertion will not prevent runtime errors.Apply this safer pattern:
- return planPeriod === 'annual' - ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - plan.annualMonthlyFee! - : plan.fee; + if (planPeriod === 'annual' && plan.annualMonthlyFee) { + return plan.annualMonthlyFee; + } + return plan.fee;packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (3)
377-379: Critical: Inconsistent field check - annualMonthlyFee vs annualFee.Line 377 checks
subscription.plan.annualMonthlyFeeto determine switchability, but lines 412-415 and 485-489 use non-null assertions onsubscription.plan.annualFee. These are different fields—one being present doesn't guarantee the other exists. This mismatch can cause runtime errors.Either verify both fields or use the same field consistently:
const isSwitchable = ((subscription.planPeriod === 'month' && Boolean(subscription.plan.annualMonthlyFee)) || subscription.planPeriod === 'annual') && subscription.status !== 'past_due'; + +// Verify annualFee exists when needed for switchable logic +if (isSwitchable && subscription.planPeriod === 'month' && !subscription.plan.annualFee) { + // Handle missing annualFee +}
412-415: Critical: Unsafe non-null assertion in localization keys.The non-null assertions
subscription.plan.annualFee!will throw ifannualFeeis null. Since the switchability check verifiesannualMonthlyFee(notannualFee), this field may be null when accessed.Add a guard before building the localization string:
+ if (isSwitchable && subscription.planPeriod === 'month' && !subscription.plan.annualFee) { + return null; // or provide fallback + } + isSwitchable ? { label: subscription.planPeriod === 'month' ? localizationKeys('billing.switchToAnnualWithAnnualPrice', { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - price: normalizeFormatted(subscription.plan.annualFee!.amountFormatted), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - currency: subscription.plan.annualFee!.currencySymbol, + price: normalizeFormatted(subscription.plan.annualFee.amountFormatted), + currency: subscription.plan.annualFee.currencySymbol, })
485-489: Critical: Unsafe non-null assertion in fee calculation.Using
subscription.plan.annualFee!without verification will throw if the field is null.Apply this safer pattern:
const fee = - subscription.planPeriod === 'month' + subscription.planPeriod === 'month' || !subscription.plan.annualFee ? subscription.plan.fee - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - subscription.plan.annualFee!; + : subscription.plan.annualFee;
🧹 Nitpick comments (2)
packages/clerk-js/src/core/resources/BillingPayer.ts (1)
30-33: Extract date parsing logic to a helper function.The nested ternary logic for preserving undefined vs null semantics when parsing dates is correct but complex and duplicated. Extract it to a reusable helper function to improve readability and maintainability.
Consider adding this helper to
@/utils/date:// In @/utils/date export function parseOptionalEpoch(epoch: number | null | undefined): Date | null | undefined { return epoch === undefined ? undefined : epoch === null ? null : unixEpochToDate(epoch); }Then simplify the fromJSON method:
- this.createdAt = - data.created_at === undefined ? undefined : data.created_at === null ? null : unixEpochToDate(data.created_at); - this.updatedAt = - data.updated_at === undefined ? undefined : data.updated_at === null ? null : unixEpochToDate(data.updated_at); + this.createdAt = parseOptionalEpoch(data.created_at); + this.updatedAt = parseOptionalEpoch(data.updated_at);packages/backend/src/api/resources/Feature.ts (1)
21-29: Update JSDoc comments to reflect nullability.The constructor parameters for
descriptionandavatarUrlare now nullable, but the JSDoc comments at lines 19 and 27 don't indicate this. Consider updating them to clarify when these values might be null.Apply this diff to update the JSDoc:
/** * The description of the feature. + * @remarks This field may be `null` if no description is provided. */ readonly description: string | null, /** * The URL-friendly identifier of the feature. */ readonly slug: string, /** * The URL of the feature's avatar image. + * @remarks This field may be `null` if no avatar is available. */ readonly avatarUrl: string | null,
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (20)
packages/backend/src/api/resources/CommercePlan.ts(4 hunks)packages/backend/src/api/resources/CommerceSubscription.ts(1 hunks)packages/backend/src/api/resources/Feature.ts(1 hunks)packages/backend/src/api/resources/JSON.ts(3 hunks)packages/clerk-js/src/core/constants.ts(1 hunks)packages/clerk-js/src/core/resources/BillingPayer.ts(2 hunks)packages/clerk-js/src/core/resources/BillingPaymentMethod.ts(2 hunks)packages/clerk-js/src/core/resources/BillingPlan.ts(2 hunks)packages/clerk-js/src/core/resources/Feature.ts(2 hunks)packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx(6 hunks)packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx(6 hunks)packages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsx(1 hunks)packages/clerk-js/src/ui/components/PaymentMethods/PaymentMethodRow.tsx(3 hunks)packages/clerk-js/src/ui/components/PaymentMethods/PaymentMethods.tsx(3 hunks)packages/clerk-js/src/ui/components/Plans/PlanDetails.tsx(2 hunks)packages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsx(1 hunks)packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx(3 hunks)packages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.ts(1 hunks)packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx(3 hunks)packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/clerk-js/src/ui/components/Subscriptions/SubscriptionsList.tsx
- packages/clerk-js/src/ui/components/PricingTable/PricingTableMatrix.tsx
- packages/clerk-js/src/ui/components/PricingTable/utils/pricing-footer-state.ts
- packages/clerk-js/src/core/constants.ts
🧰 Additional context used
📓 Path-based instructions (9)
packages/clerk-js/src/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/clerk-js-ui.mdc)
packages/clerk-js/src/ui/**/*.{ts,tsx}: Element descriptors should always be camelCase
Use element descriptors in UI components to enable consistent theming and styling via appearance.elements
Element descriptors should generate unique, stable CSS classes for theming
Element descriptors should handle state classes (e.g., cl-loading, cl-active, cl-error, cl-open) automatically based on component state
Do not render hard-coded values; all user-facing strings must be localized using provided localization methods
Use the useLocalizations hook and localizationKeys utility for all text and error messages
Use the styled system (sx prop, theme tokens, responsive values) for custom component styling
Use useCardState for card-level state, useFormState for form-level state, and useLoadingStatus for loading states
Always use handleError utility for API errors and use translateError for localized error messages
Use useFormControl for form field state, implement proper validation, and handle loading and error states in forms
Use localization keys for all form labels and placeholders
Use element descriptors for consistent styling and follow the theme token system
Use the Card and FormContainer patterns for consistent UI structure
Files:
packages/clerk-js/src/ui/components/Plans/PlanDetails.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethods.tsxpackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethodRow.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/clerk-js/src/ui/components/Plans/PlanDetails.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/backend/src/api/resources/CommerceSubscription.tspackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/core/resources/BillingPayer.tspackages/clerk-js/src/core/resources/Feature.tspackages/backend/src/api/resources/JSON.tspackages/backend/src/api/resources/Feature.tspackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethods.tsxpackages/backend/src/api/resources/CommercePlan.tspackages/clerk-js/src/core/resources/BillingPaymentMethod.tspackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethodRow.tsx
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/clerk-js/src/ui/components/Plans/PlanDetails.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/backend/src/api/resources/CommerceSubscription.tspackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/core/resources/BillingPayer.tspackages/clerk-js/src/core/resources/Feature.tspackages/backend/src/api/resources/JSON.tspackages/backend/src/api/resources/Feature.tspackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethods.tsxpackages/backend/src/api/resources/CommercePlan.tspackages/clerk-js/src/core/resources/BillingPaymentMethod.tspackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethodRow.tsx
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/clerk-js/src/ui/components/Plans/PlanDetails.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/backend/src/api/resources/CommerceSubscription.tspackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/core/resources/BillingPayer.tspackages/clerk-js/src/core/resources/Feature.tspackages/backend/src/api/resources/JSON.tspackages/backend/src/api/resources/Feature.tspackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethods.tsxpackages/backend/src/api/resources/CommercePlan.tspackages/clerk-js/src/core/resources/BillingPaymentMethod.tspackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethodRow.tsx
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/clerk-js/src/ui/components/Plans/PlanDetails.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/backend/src/api/resources/CommerceSubscription.tspackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/core/resources/BillingPayer.tspackages/clerk-js/src/core/resources/Feature.tspackages/backend/src/api/resources/JSON.tspackages/backend/src/api/resources/Feature.tspackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethods.tsxpackages/backend/src/api/resources/CommercePlan.tspackages/clerk-js/src/core/resources/BillingPaymentMethod.tspackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethodRow.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertionsfor literal types:as const
Usesatisfiesoperator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/clerk-js/src/ui/components/Plans/PlanDetails.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/backend/src/api/resources/CommerceSubscription.tspackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/core/resources/BillingPayer.tspackages/clerk-js/src/core/resources/Feature.tspackages/backend/src/api/resources/JSON.tspackages/backend/src/api/resources/Feature.tspackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethods.tsxpackages/backend/src/api/resources/CommercePlan.tspackages/clerk-js/src/core/resources/BillingPaymentMethod.tspackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethodRow.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{jsx,tsx}: Use error boundaries in React components
Minimize re-renders in React components
**/*.{jsx,tsx}: Always use functional components with hooks instead of class components
Follow PascalCase naming for components:UserProfile,NavigationMenu
Keep components focused on a single responsibility - split large components
Limit component size to 150-200 lines; extract logic into custom hooks
Use composition over inheritance - prefer smaller, composable components
Export components as named exports for better tree-shaking
One component per file with matching filename and component name
Use useState for simple state management
Use useReducer for complex state logic
Implement proper state initialization
Use proper state updates with callbacks
Implement proper state cleanup
Use Context API for theme/authentication
Implement proper state selectors
Use proper state normalization
Implement proper state persistence
Use React.memo for expensive components
Implement proper useCallback for handlers
Use proper useMemo for expensive computations
Implement proper virtualization for lists
Use proper code splitting with React.lazy
Implement proper cleanup in useEffect
Use proper refs for DOM access
Implement proper event listener cleanup
Use proper abort controllers for fetch
Implement proper subscription cleanup
Use proper HTML elements
Implement proper ARIA attributes
Use proper heading hierarchy
Implement proper form labels
Use proper button types
Implement proper focus management
Use proper keyboard shortcuts
Implement proper tab order
Use proper skip links
Implement proper focus traps
Implement proper error boundaries
Use proper error logging
Implement proper error recovery
Use proper error messages
Implement proper error fallbacks
Use proper form validation
Implement proper error states
Use proper error messages
Implement proper form submission
Use proper form reset
Use proper component naming
Implement proper file naming
Use proper prop naming
Implement proper...
Files:
packages/clerk-js/src/ui/components/Plans/PlanDetails.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethods.tsxpackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethodRow.tsx
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/clerk-js/src/ui/components/Plans/PlanDetails.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/backend/src/api/resources/CommerceSubscription.tspackages/clerk-js/src/core/resources/BillingPlan.tspackages/clerk-js/src/core/resources/BillingPayer.tspackages/clerk-js/src/core/resources/Feature.tspackages/backend/src/api/resources/JSON.tspackages/backend/src/api/resources/Feature.tspackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethods.tsxpackages/backend/src/api/resources/CommercePlan.tspackages/clerk-js/src/core/resources/BillingPaymentMethod.tspackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethodRow.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/react.mdc)
**/*.tsx: Use proper type definitions for props and state
Leverage TypeScript's type inference where possible
Use proper event types for handlers
Implement proper generic types for reusable components
Use proper type guards for conditional rendering
Files:
packages/clerk-js/src/ui/components/Plans/PlanDetails.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsxpackages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsxpackages/clerk-js/src/ui/components/PricingTable/PricingTableDefault.tsxpackages/clerk-js/src/ui/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/clerk-js/src/ui/components/SubscriptionDetails/index.tsxpackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethods.tsxpackages/clerk-js/src/ui/components/PaymentMethods/PaymentMethodRow.tsx
🧬 Code graph analysis (10)
packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx (1)
packages/shared/src/types/billing.ts (1)
BillingPaymentMethodResource(276-347)
packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx (1)
packages/shared/src/types/billing.ts (1)
BillingPaymentMethodResource(276-347)
packages/clerk-js/src/ui/components/SubscriptionDetails/index.tsx (1)
packages/clerk-js/src/ui/contexts/components/Plans.tsx (1)
normalizeFormatted(27-29)
packages/backend/src/api/resources/CommerceSubscription.ts (1)
packages/backend/src/api/resources/CommerceSubscriptionItem.ts (1)
BillingSubscriptionItem(11-128)
packages/clerk-js/src/core/resources/BillingPlan.ts (2)
packages/shared/src/types/billing.ts (2)
BillingMoneyAmount(637-654)BillingPayerResourceType(62-62)packages/clerk-js/src/utils/billing.ts (1)
billingMoneyAmountFromJSON(10-17)
packages/backend/src/api/resources/JSON.ts (1)
packages/shared/src/types/json.ts (1)
BillingMoneyAmountJSON(783-788)
packages/backend/src/api/resources/Feature.ts (2)
packages/backend/src/api/resources/JSON.ts (1)
FeatureJSON(834-840)packages/clerk-js/src/core/resources/Feature.ts (1)
Feature(5-30)
packages/clerk-js/src/ui/components/PaymentMethods/PaymentMethods.tsx (1)
packages/shared/src/types/billing.ts (1)
BillingPaymentMethodResource(276-347)
packages/backend/src/api/resources/CommercePlan.ts (3)
packages/shared/src/types/billing.ts (1)
BillingMoneyAmount(637-654)packages/backend/src/api/resources/JSON.ts (1)
BillingPlanJSON(845-861)packages/backend/src/api/resources/Feature.ts (1)
Feature(8-35)
packages/clerk-js/src/core/resources/BillingPaymentMethod.ts (1)
packages/shared/src/types/billing.ts (2)
BillingPaymentMethodResource(276-347)BillingPaymentMethodStatus(222-222)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (9)
packages/backend/src/api/resources/CommerceSubscription.ts (1)
76-76: LGTM! Good defensive pattern.The null-coalescing operator ensures safe handling when
subscription_itemsis absent from the API response, preventing runtime errors during mapping.packages/clerk-js/src/ui/components/Plans/PlanDetails.tsx (1)
230-234: LGTM! Excellent defensive pattern.The presence check for
annualMonthlyFeebefore access (lines 230-231) and before rendering the toggle (line 336) prevents runtime errors and correctly handles plans without annual pricing. This is the proper approach for nullable fields.Also applies to: 336-364
packages/backend/src/api/resources/CommercePlan.ts (2)
72-75: LGTM! Defensive null handling.The
formatAmountJSONhelper correctly returnsnullfor falsy inputs (lines 72-75), and the null-coalescing operators on lines 89 and 100 provide safe defaults for optional fields.Also applies to: 89-89, 100-100
94-96: The review comment is based on an incorrect assumption about the API contract.The
BillingPlanJSONinterface definesfee: BillingMoneyAmountJSON;as a required field (not optional and not nullable), whereas the optional fields likeannual_feeare properly marked with?and| null. Sincedata.feeis guaranteed to be non-null from the API contract, the non-null assertion at line 94 is safe and justified.While the
formatAmountJSONfunction accepts nullable inputs (to handle both required and optional fields), the actual call withdata.feewill never result in a null return. The assertion correctly reflects the caller's knowledge of the API contract.packages/clerk-js/src/core/resources/Feature.ts (1)
8-10: LGTM! Consistent nullable field handling.The nullable field declarations with
nulldefaults (lines 8, 10) paired with null-coalescing assignment (lines 24, 26) provide safe, predictable behavior when fields are absent from API responses.Also applies to: 24-26
packages/clerk-js/src/core/resources/BillingPlan.ts (1)
16-18: LGTM! Well-structured nullable field pattern.The nullable field declarations with
nulldefaults (lines 16-18, 25) combined with conditional transformations (lines 43-44) and null-coalescing (lines 45, 52) provide consistent, safe handling of optional billing plan fields.Also applies to: 25-25, 43-45, 52-52
packages/backend/src/api/resources/JSON.ts (2)
889-889: LGTM - Nullable lifetime_paid field.The
lifetime_paidfield is now nullable but still required (no?operator). This correctly models that the field must always be present in the response but can have a null value (e.g., when no payments have been made yet).
837-839: Nullability changes verified and properly handled downstream.The changes to
FeatureJSONmakingdescriptionandavatar_urloptional and nullable (lines 837, 839) are correctly applied. Verification confirms:
- Backend
Feature.fromJSON()uses defensive nullish coalescing (??) to safely convert undefined to null- Frontend
PlanDetails.tsxincludes proper conditional checks (feature.description ?) before rendering- No unsafe direct property access patterns found in non-test code
The implementation is sound and ready.
packages/backend/src/api/resources/Feature.ts (1)
33-33: LGTM - Proper null coalescing in fromJSON.The use of
?? nullensures that undefined values are normalized to null, which is consistent with the type declarations (string | null). This defensive approach prevents undefined from leaking into properties that should be explicitly null.
| description?: string | null; | ||
| is_default: boolean; | ||
| is_recurring: boolean; | ||
| has_base_fee: boolean; | ||
| publicly_visible: boolean; | ||
| fee: BillingMoneyAmountJSON; | ||
| annual_fee: BillingMoneyAmountJSON; | ||
| annual_monthly_fee: BillingMoneyAmountJSON; | ||
| annual_fee?: BillingMoneyAmountJSON | null; | ||
| annual_monthly_fee?: BillingMoneyAmountJSON | null; | ||
| for_payer_type: 'org' | 'user'; | ||
| features: FeatureJSON[]; | ||
| features?: FeatureJSON[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify impact of billing plan field nullability changes.
The changes make several previously required billing fields optional and/or nullable:
description: optional + nullableannual_fee,annual_monthly_fee: optional + nullable (previously required)features: optional (previously required array)
The removal of required status from annual_fee and annual_monthly_fee is a significant breaking change. Verify that all consumers check for null/undefined before accessing these fields.
🏁 Script executed:
#!/bin/bash
# Description: Find usages of BillingPlanJSON fields to check for null safety
# Search for annual_fee and annual_monthly_fee access patterns
rg -nP --type=ts --type=tsx -C3 '\b(plan|billing)\.annual_(fee|monthly_fee)\b' -g '!**/test/**' -g '!**/*.test.ts'
# Search for features array access
rg -nP --type=ts --type=tsx -C3 '\b(plan|billing)\.features(\[|\.)' -g '!**/test/**' -g '!**/*.test.ts'Length of output: 55
I'll fix the script syntax and re-verify the field usages in the codebase.
🏁 Script executed:
#!/bin/bash
# Description: Find usages of BillingPlanJSON fields to check for null safety
# Fixed: Use glob patterns instead of unrecognized type flags
# Search for annual_fee and annual_monthly_fee access patterns in TypeScript files
rg -nP -C3 '\b(plan|billing)\.annual_(fee|monthly_fee)\b' --glob '*.ts' --glob '*.tsx' -g '!**/test/**' -g '!**/*.test.ts'
# Search for features array access
rg -nP -C3 '\b(plan|billing)\.features(\[|\.)' --glob '*.ts' --glob '*.tsx' -g '!**/test/**' -g '!**/*.test.ts'
# Also search for optional chaining patterns which might indicate null safety
rg -nP -C3 '\b(plan|billing)\?.annual_(fee|monthly_fee)' --glob '*.ts' --glob '*.tsx' -g '!**/test/**' -g '!**/*.test.ts'Length of output: 3348
Let me search more broadly for the annual fee fields and verify the type definitions:
🏁 Script executed:
#!/bin/bash
# Search for broader annual_fee patterns
rg -nP -C2 'annual_fee|annual_monthly_fee' --glob '*.ts' --glob '*.tsx' -g '!**/test/**' -g '!**/*.test.ts' | head -50
# Find the BillingPlanJSON type definition
rg -nP -C10 'interface BillingPlanJSON|type BillingPlanJSON' --glob '*.ts' --glob '*.tsx'
# Check for any null/undefined guards on plan.features
rg -nP -C3 'plan\.features\s*\?' --glob '*.ts' --glob '*.tsx' -g '!**/test/**' -g '!**/*.test.ts'
rg -nP -C3 'plan\.features\s*\|\|' --glob '*.ts' --glob '*.tsx' -g '!**/test/**' -g '!**/*.test.ts'Length of output: 5493
Let me check for additional annual_fee usages in the backend:
🏁 Script executed:
#!/bin/bash
# Search for formatAmountJSON calls with annual_fee/annual_monthly_fee
rg -nP -C5 'formatAmountJSON\s*\(\s*data\.(annual_fee|annual_monthly_fee)' --glob '*.ts' --glob '*.tsx'
# Check if there are type guards or defaults being set for features
rg -nP -C3 'features\s*\?\?' --glob '*.ts' --glob '*.tsx' -g '!**/test/**'
# Search for any feature array defaults or initialization
rg -nP -C2 '\[\]\s*as.*Feature|Feature\[\].*=' --glob '*.ts' --glob '*.tsx' | head -30Length of output: 1664
Fix inconsistent null handling for optional billing plan fields.
The change makes annual_fee, annual_monthly_fee, and features optional, but the implementation is inconsistent:
-
CommercePlan.ts lines 97-98:
formatAmountJSON(data.annual_fee)andformatAmountJSON(data.annual_monthly_fee)lack null coalescing. Compare to line 100 where features usesdata.features ?? []. Apply the same pattern:formatAmountJSON(data.annual_fee ?? null)andformatAmountJSON(data.annual_monthly_fee ?? null)(or appropriate fallback). -
PricingTableMatrix.tsx:378, PricingTableDefault.tsx:133/431/456: Multiple accesses to
plan.featureswithout optional chaining (?.) or null guards. These will throw if features is undefined.
🤖 Prompt for AI Agents
In packages/backend/src/api/resources/JSON.ts lines 851-860 and related places,
fix inconsistent null handling for optional billing plan fields: in
CommercePlan.ts around lines 97-100, wrap calls to formatAmountJSON with a
null-coalescing fallback (e.g. call formatAmountJSON(data.annual_fee ?? null)
and formatAmountJSON(data.annual_monthly_fee ?? null)) to mirror the existing
features fallback; in PricingTableMatrix.tsx line ~378 and
PricingTableDefault.tsx lines ~133, ~431, ~456, guard accesses to plan.features
with optional chaining or explicit null checks (e.g. plan.features ?? [] or
plan.features?.map/length checks) so code never assumes features is defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/clerk-js/src/core/modules/billing/namespace.ts (1)
32-36: LGTM! Consider adding JSDoc documentation.The signature change to accept
orgId?: string | nullcorrectly aligns with the API version update to 2025-10-01 and broader nullability support. The implementation properly handles bothnullandundefinedwith the truthy check on line 34, defaulting to/mefor both cases.Consider adding JSDoc documentation for this public static method to improve API discoverability:
+ /** + * Constructs a billing API path with optional organization context. + * @param subPath - The billing resource subpath (e.g., '/subscription', '/statements') + * @param param - Optional parameters + * @param param.orgId - Organization ID. When null or undefined, defaults to '/me' prefix for user context. + * @returns Complete billing API path + */ static path(subPath: string, param?: { orgId?: string | null }): string {As per coding guidelines.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
packages/clerk-js/src/core/modules/billing/namespace.ts(1 hunks)packages/clerk-js/src/core/resources/BillingPayment.ts(2 hunks)packages/clerk-js/src/core/resources/BillingSubscription.ts(0 hunks)packages/shared/src/types/billing.ts(11 hunks)packages/shared/src/types/json.ts(7 hunks)
💤 Files with no reviewable changes (1)
- packages/clerk-js/src/core/resources/BillingSubscription.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/core/modules/billing/namespace.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/core/modules/billing/namespace.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/core/modules/billing/namespace.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/core/modules/billing/namespace.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertionsfor literal types:as const
Usesatisfiesoperator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/core/modules/billing/namespace.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/core/modules/billing/namespace.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.ts
🧬 Code graph analysis (2)
packages/clerk-js/src/core/resources/BillingPayment.ts (2)
packages/shared/src/types/billing.ts (1)
BillingPaymentMethodResource(276-347)packages/clerk-js/src/core/resources/BillingPaymentMethod.ts (1)
BillingPaymentMethod(17-82)
packages/shared/src/types/json.ts (1)
packages/shared/src/types/billing.ts (2)
BillingPayerResourceType(62-62)BillingPaymentMethodStatus(222-222)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (1)
packages/clerk-js/src/core/resources/BillingPayment.ts (1)
21-42: Guard prevents crashes when payment methods are omitted.Wrapping the
BillingPaymentMethodconstruction behind a truthy check keeps us from instantiating withundefinedwhen the backend omits or nullspayment_method, so the resource stays resilient against the expanded API shape. Nice touch.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (2)
packages/clerk-js/src/core/resources/BillingPayer.ts (1)
9-17: Unresolved: Inconsistent null handling across field types.The past review comments correctly identified this issue, and it remains unresolved. The field declarations suggest three-state semantics (
field?: Type | null), but the implementation is inconsistent:
- Date fields (lines 30-35): Preserve
undefinedvsnull(three-state)- imageUrl (line 36): Preserves
undefinedvsnullvia direct assignment (three-state)- String fields (lines 37-42): Collapse
undefinedtonullusing?? null(two-state)As noted in previous reviews, this should be made consistent. Either:
- Option A (Recommended): Remove
?? nullfrom lines 37-42 to preserve three-state semantics for all fields- Option B: Apply
?? nullconsistently to all fields (includingimageUrl) and document why date fields need different treatmentApply this diff to make string field handling consistent with date fields:
this.imageUrl = data.image_url; - this.userId = data.user_id ?? null; - this.email = data.email ?? null; - this.firstName = data.first_name ?? null; - this.lastName = data.last_name ?? null; - this.organizationId = data.organization_id ?? null; - this.organizationName = data.organization_name ?? null; + this.userId = data.user_id === undefined ? undefined : data.user_id; + this.email = data.email === undefined ? undefined : data.email; + this.firstName = data.first_name === undefined ? undefined : data.first_name; + this.lastName = data.last_name === undefined ? undefined : data.last_name; + this.organizationId = data.organization_id === undefined ? undefined : data.organization_id; + this.organizationName = data.organization_name === undefined ? undefined : data.organization_name;Also applies to: 30-42
packages/shared/src/types/billing.ts (1)
134-140: This concern was already raised in a previous review.The nullable type changes for
annualFeeandannualMonthlyFeewere flagged previously, with specific consumer code locations identified that use non-null assertions (!) and will break type safety. Please refer to the existing review comment for details on the affected files and suggested solutions.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (11)
packages/clerk-js/src/core/constants.ts(1 hunks)packages/clerk-js/src/core/resources/BillingCheckout.ts(2 hunks)packages/clerk-js/src/core/resources/BillingPayer.ts(2 hunks)packages/clerk-js/src/core/resources/BillingPayment.ts(2 hunks)packages/clerk-js/src/core/resources/BillingPaymentMethod.ts(2 hunks)packages/clerk-js/src/core/resources/BillingSubscription.ts(2 hunks)packages/clerk-js/src/core/resources/CommerceSettings.ts(2 hunks)packages/clerk-js/src/utils/billing.ts(1 hunks)packages/shared/src/types/billing.ts(13 hunks)packages/shared/src/types/commerceSettings.ts(2 hunks)packages/shared/src/types/json.ts(9 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/clerk-js/src/core/constants.ts
- packages/clerk-js/src/utils/billing.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/clerk-js/src/core/resources/BillingCheckout.tspackages/clerk-js/src/core/resources/BillingPayer.tspackages/clerk-js/src/core/resources/CommerceSettings.tspackages/clerk-js/src/core/resources/BillingSubscription.tspackages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/core/resources/BillingPaymentMethod.tspackages/shared/src/types/commerceSettings.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/clerk-js/src/core/resources/BillingCheckout.tspackages/clerk-js/src/core/resources/BillingPayer.tspackages/clerk-js/src/core/resources/CommerceSettings.tspackages/clerk-js/src/core/resources/BillingSubscription.tspackages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/core/resources/BillingPaymentMethod.tspackages/shared/src/types/commerceSettings.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/clerk-js/src/core/resources/BillingCheckout.tspackages/clerk-js/src/core/resources/BillingPayer.tspackages/clerk-js/src/core/resources/CommerceSettings.tspackages/clerk-js/src/core/resources/BillingSubscription.tspackages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/core/resources/BillingPaymentMethod.tspackages/shared/src/types/commerceSettings.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/clerk-js/src/core/resources/BillingCheckout.tspackages/clerk-js/src/core/resources/BillingPayer.tspackages/clerk-js/src/core/resources/CommerceSettings.tspackages/clerk-js/src/core/resources/BillingSubscription.tspackages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/core/resources/BillingPaymentMethod.tspackages/shared/src/types/commerceSettings.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertionsfor literal types:as const
Usesatisfiesoperator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/clerk-js/src/core/resources/BillingCheckout.tspackages/clerk-js/src/core/resources/BillingPayer.tspackages/clerk-js/src/core/resources/CommerceSettings.tspackages/clerk-js/src/core/resources/BillingSubscription.tspackages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/core/resources/BillingPaymentMethod.tspackages/shared/src/types/commerceSettings.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/clerk-js/src/core/resources/BillingCheckout.tspackages/clerk-js/src/core/resources/BillingPayer.tspackages/clerk-js/src/core/resources/CommerceSettings.tspackages/clerk-js/src/core/resources/BillingSubscription.tspackages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/core/resources/BillingPaymentMethod.tspackages/shared/src/types/commerceSettings.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.ts
🧬 Code graph analysis (4)
packages/clerk-js/src/core/resources/BillingSubscription.ts (2)
packages/shared/src/types/billing.ts (2)
BillingMoneyAmount(629-646)BillingSubscriptionItemResource(501-565)packages/clerk-js/src/utils/billing.ts (1)
billingMoneyAmountFromJSON(10-17)
packages/clerk-js/src/core/resources/BillingPayment.ts (2)
packages/shared/src/types/billing.ts (1)
BillingPaymentMethodResource(275-344)packages/clerk-js/src/core/resources/BillingPaymentMethod.ts (1)
BillingPaymentMethod(17-79)
packages/clerk-js/src/core/resources/BillingPaymentMethod.ts (1)
packages/shared/src/types/billing.ts (2)
BillingPaymentMethodResource(275-344)BillingPaymentMethodStatus(222-222)
packages/shared/src/types/json.ts (1)
packages/shared/src/types/billing.ts (2)
BillingPayerResourceType(62-62)BillingPaymentMethodStatus(222-222)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (13)
packages/shared/src/types/commerceSettings.ts (1)
7-7: LGTM! Nullable Stripe key aligns with API version update.The widening of
stripe_publishable_keyto allownullis appropriate for the 2025-10-01 API version. This aligns with the implementation inpackages/clerk-js/src/core/resources/CommerceSettings.tswhich now defaults tonullinstead of an empty string.Also applies to: 21-21
packages/shared/src/types/json.ts (1)
622-629: LGTM! Extensive nullability updates align with API version 2025-10-01.The changes appropriately widen types to support nullable/optional fields across billing entities. The pattern is generally consistent:
- Optional-nullable (
field?: Type | null) for fields that may be absent or explicitly null- Optional-only (
field?: Type) for fields that are simply absent when not applicable (e.g.,free_trial_ends_at)This distinction appears intentional and aligns with the broader API surface changes across the PR.
Also applies to: 634-671, 707-718, 782-799, 804-840
packages/clerk-js/src/core/resources/BillingSubscription.ts (1)
26-29: Good use of optional pattern for conditional field.The change from
nextPayment: object | nulltonextPayment?: objectcorrectly usesundefinedto indicate absence. ThefromJSONlogic only assigns whendata.next_paymentexists, leaving the fieldundefinedotherwise. This is a cleaner pattern than explicitnullassignment.Note: This differs from other resources in this PR (e.g.,
BillingPayment) which use nullable with explicitnulldefaults. While both approaches are valid, consistency across the billing domain would improve maintainability.Also applies to: 50-55
packages/clerk-js/src/core/resources/BillingCheckout.ts (1)
31-31: LGTM! Consistent optional pattern for conditional field.The change from
freeTrialEndsAt: Date | nulltofreeTrialEndsAt?: Datefollows the same pattern asBillingSubscription.nextPayment, usingundefinedfor absence. The conditional assignment infromJSONcorrectly leaves the fieldundefinedwhen not present.Also applies to: 55-57
packages/clerk-js/src/core/resources/CommerceSettings.ts (1)
10-10: LGTM! Default change aligns with nullable type.The default value change from
''(empty string) tonullaligns with the type definition update inpackages/shared/src/types/commerceSettings.ts. The direct assignment infromJSONwithout fallbacks is appropriate given thenulldefault initialization.Also applies to: 31-35
packages/clerk-js/src/core/resources/BillingPaymentMethod.ts (1)
19-29: LGTM! Proper three-state handling for optional-nullable fields.The new fields and type changes appropriately support the expanded API surface. The
fromJSONdate handling correctly preserves the distinction betweenundefined(field absent) andnull(field present but null):this.createdAt = data.created_at == null ? data.created_at : unixEpochToDate(data.created_at);This pattern is consistent with the field declarations (
createdAt?: Date | null) and maintains proper three-state semantics.Also applies to: 36-55
packages/clerk-js/src/core/resources/BillingPayment.ts (1)
18-21: Inconsistency confirmed; standardization decision depends on API contract.The null-handling pattern inconsistency is verified:
BillingPaymentuses explicitDate | null = nulldefaults (required fields that can be null)- Other billing resources use
?: Typepattern (optional fields)BillingPaymentMethoduses a hybrid?: Date | nullapproachThe explicit null defaults in
BillingPaymentare semantically justified—the initialization code shows conditional assignment to null:this.paidAt = data.paid_at ? unixEpochToDate(data.paid_at) : null, confirming these fields legitimately can be null.The inconsistency creates confusion. To resolve, determine the standard across billing resources:
- If all nullable fields should be explicit (
Type | null = null), update other resources to match- If all should be optional-or-nullable (
?: Type | null), updateBillingPaymentto match- Verify the API contract to ensure the chosen pattern reflects actual response behavior
packages/shared/src/types/billing.ts (6)
689-702: Good refactor to explicit interface definition.Replacing the Omit-based structure with an explicit interface definition for
BillingStatementTotalsimproves type clarity and maintainability.
720-754: The verification confirms that thepaymentSourceId→paymentMethodIdrename has been fully completed across the codebase with no remaining references to the old parameter name. All consumer code and tests have been updated consistently.
283-323: Consumer code already handles optionality changes with defensive patterns.Verification confirms all consumer code uses proper defensive patterns for the optional fields:
- Nullish coalescing on
paymentType,cardType,last4:paymentMethod.paymentType ?? 'card'- Boolean fields correctly handled as falsy when undefined:
if (!paymentMethod.isDefault),{paymentMethod.isDefault && ...}- Sorting explicitly converts to boolean:
a.isDefault ?? false- The
'link'payment type removal has zero impact (no usages in codebase)No breaking changes detected in existing consumers.
395-407: No issues found — nullable field changes are properly handled by consumers.Verification confirms that the nullable changes to
paidAt,failedAt, andpaymentMethodfields inBillingPaymentResourceare safe:
paidAtandfailedAtare consistently guarded with OR operators that fall back to the non-nullableupdatedAtfield in PaymentAttempts components.- No unsafe null dereferences were found for the
paymentMethodfield.- The
BillingPaymentclass implementation already declares these fields as nullable with null defaults, ensuring consistency with the type definition.
669-681: Nullable field handling in BillingCheckoutTotals is already properly implemented.Verification confirms all consumer code correctly handles the nullable
creditandpastDuefields using optional chaining with short-circuit evaluation (CheckoutForm.tsx:57-58), andtotalDueAfterFreeTrialis properly deserialized with null checks (billing.ts:40-42). No unsafe direct property accesses exist.
204-214: Consumer code properly handles nullabledescriptionandavatarUrlfields.Verification confirms that:
- Both
Featureclass implementations (clerk-js and backend) correctly initialize these fields asnulland handle null coercion in theirfromJSON()methods- The primary consumer in
PlanDetails.tsxuses proper null-checks (feature.avatarUrl ? (andfeature.description ? () before rendering these fields- No unsafe direct property access patterns were found
The implementation is type-safe and correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/clerk-js/src/core/resources/Feature.ts (1)
24-26: Add null-coalescing to handle optional JSON fields.The fields
descriptionandavatarUrlare typed asstring | null, butFeatureJSONlikely defines them as optional (description?: string | null). When these fields are missing from the JSON,data.descriptionanddata.avatar_urlwill beundefined, which doesn't match the field type. This creates a type inconsistency that can lead to runtime bugs if other code expects these fields to bestring | null(neverundefined).Apply this diff to normalize
undefinedtonull:this.id = data.id; this.name = data.name; - this.description = data.description; + this.description = data.description ?? null; this.slug = data.slug; - this.avatarUrl = data.avatar_url; + this.avatarUrl = data.avatar_url ?? null;packages/clerk-js/src/core/resources/BillingPayer.ts (1)
24-44: Inconsistent null-to-undefined conversion between field types.The
fromJSONmethod handles null values differently across field types:
- Date fields (lines 30–35): Use if-guards that convert both
nullandundefinedtoundefined(field not assigned), matching their type signaturecreatedAt?: Datewhich excludes null.- String fields (lines 36–42): Use direct assignment that preserves
nullasnullandundefinedasundefined, creating three-state semantics.This inconsistency makes the intent unclear. While date fields explicitly normalize
null→undefinedto match their types, string fields preserve whatever the API returns. This creates different null-handling semantics across the resource.Consider standardizing the approach:
Option 1 (Recommended): Make string field handling explicit to match the intent of date fields:
- this.imageUrl = data.image_url; - this.userId = data.user_id; - this.email = data.email; - this.firstName = data.first_name; - this.lastName = data.last_name; - this.organizationId = data.organization_id; - this.organizationName = data.organization_name; + this.imageUrl = data.image_url === undefined ? undefined : data.image_url; + this.userId = data.user_id ?? null; + this.email = data.email === undefined ? undefined : data.email ?? null; + this.firstName = data.first_name === undefined ? undefined : data.first_name ?? null; + this.lastName = data.last_name === undefined ? undefined : data.last_name ?? null; + this.organizationId = data.organization_id ?? null; + this.organizationName = data.organization_name === undefined ? undefined : data.organization_name ?? null;This preserves undefined for optional fields while converting null appropriately for each field's type signature.
Option 2: Document the three-state semantics as intentional and ensure the API contract and downstream consumers handle all three states correctly.
Based on past review comments
🧹 Nitpick comments (3)
integration/tests/pricing-table.test.ts (1)
845-874: Well-designed test helpers with clear documentation.The helper functions improve test readability and maintainability. The use of Clerk-specific CSS classes and XPath selectors is appropriate for integration tests, though it does couple the tests to the component's DOM structure.
Per the TypeScript coding guidelines, consider adding explicit return types and parameter types for better type safety and documentation:
-function matchLineItem(root: Locator, title: string | RegExp, description?: string | RegExp): Locator { +function matchLineItem(root: Locator, title: string | RegExp, description?: string | RegExp): Locator { // Find the title element using the Clerk-generated class const titleElement = root.locator('.cl-lineItemsTitle').filter({ hasText: title }); // If no description is provided, return the title element if (description === undefined) { return titleElement; } // Get the next sibling description element using the Clerk-generated class const descriptionElement = titleElement .locator('xpath=following-sibling::*[1][contains(@class, "cl-lineItemsDescription")]') .filter({ hasText: description }); return descriptionElement; } /** * Helper to count the number of line items within a given root element. * Line items are rendered as Clerk LineItems components where each .cl-lineItemsTitle represents a line item. */ -async function countLineItems(root: Locator): Promise<number> { +async function countLineItems(root: Locator): Promise<number> { return await root.locator('.cl-lineItemsTitle').count(); }Note: The return types are already present, but you may want to verify parameter types are correctly inferred throughout usage.
packages/testing/src/playwright/unstable/page-objects/statement.ts (1)
9-33: Consider consistent access to the root locator.Methods access the root locator inconsistently—some use the
rootconst directly (lines 10, 13, 20) while others useself.root(lines 23, 29, 32). While both approaches work, usingself.rootconsistently would be clearer.Example for lines 10, 13:
waitForMounted: () => { - return root.waitFor({ state: 'visible', timeout: 15000 }); + return self.root.waitFor({ state: 'visible', timeout: 15000 }); }, waitForUnmounted: () => { - return root.waitFor({ state: 'detached', timeout: 15000 }); + return self.root.waitFor({ state: 'detached', timeout: 15000 }); },packages/testing/src/playwright/unstable/page-objects/paymentAttempt.ts (1)
9-31: Consider consistent access to the root locator.Methods access the root locator inconsistently—some use the
rootconst directly (lines 10, 13, 20) while others useself.root(lines 23, 26, 29). While both approaches work, usingself.rootconsistently would be clearer.Example for lines 10, 13:
waitForMounted: () => { - return root.waitFor({ state: 'visible', timeout: 15000 }); + return self.root.waitFor({ state: 'visible', timeout: 15000 }); }, waitForUnmounted: () => { - return root.waitFor({ state: 'detached', timeout: 15000 }); + return self.root.waitFor({ state: 'detached', timeout: 15000 }); },
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (12)
integration/tests/pricing-table.test.ts(5 hunks)packages/clerk-js/src/core/resources/BillingPayer.ts(2 hunks)packages/clerk-js/src/core/resources/BillingPlan.ts(2 hunks)packages/clerk-js/src/core/resources/Feature.ts(1 hunks)packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx(2 hunks)packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx(4 hunks)packages/clerk-js/src/ui/components/PaymentMethods/PaymentMethodRow.tsx(0 hunks)packages/shared/src/react/hooks/useCheckout.ts(1 hunks)packages/testing/src/playwright/unstable/page-objects/index.ts(2 hunks)packages/testing/src/playwright/unstable/page-objects/paymentAttempt.ts(1 hunks)packages/testing/src/playwright/unstable/page-objects/statement.ts(1 hunks)packages/testing/src/playwright/unstable/page-objects/userProfile.ts(1 hunks)
💤 Files with no reviewable changes (1)
- packages/clerk-js/src/ui/components/PaymentMethods/PaymentMethodRow.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx
- packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
- packages/clerk-js/src/core/resources/BillingPlan.ts
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/testing/src/playwright/unstable/page-objects/paymentAttempt.tspackages/testing/src/playwright/unstable/page-objects/statement.tspackages/clerk-js/src/core/resources/Feature.tsintegration/tests/pricing-table.test.tspackages/testing/src/playwright/unstable/page-objects/userProfile.tspackages/testing/src/playwright/unstable/page-objects/index.tspackages/shared/src/react/hooks/useCheckout.tspackages/clerk-js/src/core/resources/BillingPayer.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/testing/src/playwright/unstable/page-objects/paymentAttempt.tspackages/testing/src/playwright/unstable/page-objects/statement.tspackages/clerk-js/src/core/resources/Feature.tsintegration/tests/pricing-table.test.tspackages/testing/src/playwright/unstable/page-objects/userProfile.tspackages/testing/src/playwright/unstable/page-objects/index.tspackages/shared/src/react/hooks/useCheckout.tspackages/clerk-js/src/core/resources/BillingPayer.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/testing/src/playwright/unstable/page-objects/paymentAttempt.tspackages/testing/src/playwright/unstable/page-objects/statement.tspackages/clerk-js/src/core/resources/Feature.tspackages/testing/src/playwright/unstable/page-objects/userProfile.tspackages/testing/src/playwright/unstable/page-objects/index.tspackages/shared/src/react/hooks/useCheckout.tspackages/clerk-js/src/core/resources/BillingPayer.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/testing/src/playwright/unstable/page-objects/paymentAttempt.tspackages/testing/src/playwright/unstable/page-objects/statement.tspackages/clerk-js/src/core/resources/Feature.tspackages/testing/src/playwright/unstable/page-objects/userProfile.tspackages/testing/src/playwright/unstable/page-objects/index.tspackages/shared/src/react/hooks/useCheckout.tspackages/clerk-js/src/core/resources/BillingPayer.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertionsfor literal types:as const
Usesatisfiesoperator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/testing/src/playwright/unstable/page-objects/paymentAttempt.tspackages/testing/src/playwright/unstable/page-objects/statement.tspackages/clerk-js/src/core/resources/Feature.tsintegration/tests/pricing-table.test.tspackages/testing/src/playwright/unstable/page-objects/userProfile.tspackages/testing/src/playwright/unstable/page-objects/index.tspackages/shared/src/react/hooks/useCheckout.tspackages/clerk-js/src/core/resources/BillingPayer.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/testing/src/playwright/unstable/page-objects/paymentAttempt.tspackages/testing/src/playwright/unstable/page-objects/statement.tspackages/clerk-js/src/core/resources/Feature.tsintegration/tests/pricing-table.test.tspackages/testing/src/playwright/unstable/page-objects/userProfile.tspackages/testing/src/playwright/unstable/page-objects/index.tspackages/shared/src/react/hooks/useCheckout.tspackages/clerk-js/src/core/resources/BillingPayer.ts
integration/**
📄 CodeRabbit inference engine (.cursor/rules/global.mdc)
Framework integration templates and E2E tests should be placed under the integration/ directory
Files:
integration/tests/pricing-table.test.ts
integration/**/*
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
End-to-end tests and integration templates must be located in the 'integration/' directory.
Files:
integration/tests/pricing-table.test.ts
integration/**/*.{test,spec}.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Integration tests should use Playwright.
Files:
integration/tests/pricing-table.test.ts
packages/**/index.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use tree-shaking friendly exports
Files:
packages/testing/src/playwright/unstable/page-objects/index.ts
**/index.ts
📄 CodeRabbit inference engine (.cursor/rules/react.mdc)
Use index.ts files for clean imports but avoid deep barrel exports
Avoid barrel files (index.ts re-exports) as they can cause circular dependencies
Files:
packages/testing/src/playwright/unstable/page-objects/index.ts
🧬 Code graph analysis (2)
integration/tests/pricing-table.test.ts (1)
integration/testUtils/index.ts (1)
createTestUtils(24-86)
packages/testing/src/playwright/unstable/page-objects/index.ts (2)
packages/testing/src/playwright/unstable/page-objects/statement.ts (1)
createStatementPageObject(4-38)packages/testing/src/playwright/unstable/page-objects/paymentAttempt.ts (1)
createPaymentAttemptPageObject(4-35)
🪛 ast-grep (0.39.7)
integration/tests/pricing-table.test.ts
[warning] 730-730: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(date, 'i')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Build Packages
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (7)
integration/tests/pricing-table.test.ts (4)
1-1: LGTM: Type-only import follows best practices.Properly uses
import typefor theLocatortype, which improves tree-shaking and aligns with TypeScript conventions.
282-296: LGTM: Refactored assertions improve test maintainability.The use of
matchLineItemandcountLineItemshelpers makes the test more readable and resilient to minor DOM changes. The regex patterns correctly match dynamic trial period text, and the exact count verification ensures no unexpected line items appear.
356-368: LGTM: Consistent use of helpers throughout the test.The assertions correctly verify both the presence of expected line items and the absence of trial-related items after conversion to a paid subscription. The count verification adds valuable coverage.
673-775: Excellent test coverage for billing history flows.This test comprehensively validates the billing history navigation, including statements, payment attempts, and UI state transitions. The test properly exercises both successful and failed payment scenarios and uses appropriate cleanup patterns.
The ReDoS concern flagged at line 730 is confirmed to be a false positive. The
toLocaleDateString()produces a simple, predictable format (November 2025), resulting in a safe regex pattern that cannot be exploited for ReDoS attacks.packages/clerk-js/src/core/resources/Feature.ts (1)
8-10: Verify null-coalescing infromJSONfor nullable fields.The change to make
descriptionandavatarUrlnullable is appropriate for handling optional API fields. However, thefromJSONmethod (lines 24, 26) assigns these values directly without null-coalescing. IfFeatureJSONdefines these as optional (description?: string | null), thendata.descriptioncould beundefined, but the field is typed asstring | null(excludingundefined).Ensure the
fromJSONmethod uses null-coalescing to normalizeundefinedtonull.packages/shared/src/react/hooks/useCheckout.ts (1)
117-117: LGTM! The addition ofplanPeriodStart: nullis correct and type-safe.Verification confirms that
planPeriodStartis present onBillingCheckoutResourceand properly included inCheckoutProperties(which derives fromBillingCheckoutResourceminus onlypathRootandstatus). The null initialization follows the correct pattern, enforced by thesatisfies ForceNull<CheckoutProperties>constraint at line 118, and the spread operator at line 126 will automatically include this property when checkout data is available.packages/testing/src/playwright/unstable/page-objects/index.ts (1)
12-13: LGTM! New page objects properly integrated.The new statement and paymentAttempt page objects are correctly imported and exported, following the established pattern in this file.
Also applies to: 47-48
packages/testing/src/playwright/unstable/page-objects/paymentAttempt.ts
Outdated
Show resolved
Hide resolved
packages/testing/src/playwright/unstable/page-objects/statement.ts
Outdated
Show resolved
Hide resolved
packages/testing/src/playwright/unstable/page-objects/userProfile.ts
Outdated
Show resolved
Hide resolved
packages/testing/src/playwright/unstable/page-objects/userProfile.ts
Outdated
Show resolved
Hide resolved
packages/testing/src/playwright/unstable/page-objects/userProfile.ts
Outdated
Show resolved
Hide resolved
packages/testing/src/playwright/unstable/page-objects/userProfile.ts
Outdated
Show resolved
Hide resolved
packages/testing/src/playwright/unstable/page-objects/userProfile.ts
Outdated
Show resolved
Hide resolved
This reverts commit 820be73.
…end-to-use-the-new-api-versions
2025-10-012025-11-10
…end-to-use-the-new-api-versions
…end-to-use-the-new-api-versions
Description
Checklist
pnpm testruns as expected.pnpm buildruns as expected.Type of change
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Tests